/*
 * Copyright (c) 1997, 2003, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package com.sun.corba.se.impl.oa.poa;

import java.util.Set;
import java.util.HashSet;
import java.util.Map;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Vector;

import org.omg.PortableServer.Servant;
import org.omg.PortableServer.POAPackage.WrongPolicy;
import org.omg.CORBA.INTERNAL;

/**
 * The ActiveObjectMap maintains associations between servants and
 * their keys.  There are two variants, to support whether or not
 * multiple IDs per servant are allowed.  This class suppots bidirectional
 * traversal of the key-servant association.  Access to an instance of this
 * class is serialized by the POA mutex.
 */
public abstract class ActiveObjectMap {

  public static class Key {

    public byte[] id;

    Key(byte[] id) {
      this.id = id;
    }

    public String toString() {
      StringBuffer buffer = new StringBuffer();
      for (int i = 0; i < id.length; i++) {
        buffer.append(Integer.toString((int) id[i], 16));
        if (i != id.length - 1) {
          buffer.append(":");
        }
      }
      return buffer.toString();
    }

    public boolean equals(java.lang.Object key) {
      if (!(key instanceof Key)) {
        return false;
      }
      Key k = (Key) key;
      if (k.id.length != this.id.length) {
        return false;
      }
      for (int i = 0; i < this.id.length; i++) {
        if (this.id[i] != k.id[i]) {
          return false;
        }
      }
      return true;
    }

    // Use the same hash function as for String
    public int hashCode() {
      int h = 0;
      for (int i = 0; i < id.length; i++) {
        h = 31 * h + id[i];
      }
      return h;
    }
  }

  protected POAImpl poa;

  protected ActiveObjectMap(POAImpl poa) {
    this.poa = poa;
  }

  public static ActiveObjectMap create(POAImpl poa, boolean multipleIDsAllowed) {
    if (multipleIDsAllowed) {
      return new MultipleObjectMap(poa);
    } else {
      return new SingleObjectMap(poa);
    }
  }

  private Map keyToEntry = new HashMap();     // Map< Key, AOMEntry >
  private Map entryToServant = new HashMap(); // Map< AOMEntry, Servant >
  private Map servantToEntry = new HashMap(); // Map< Servant, AOMEntry >

  public final boolean contains(Servant value) {
    return servantToEntry.containsKey(value);
  }

  public final boolean containsKey(Key key) {
    return keyToEntry.containsKey(key);
  }

  /**
   * get Returbs the entry assigned to the key, or creates a new
   * entry in state INVALID if none is present.
   */
  public final AOMEntry get(Key key) {
    AOMEntry result = (AOMEntry) keyToEntry.get(key);
    if (result == null) {
      result = new AOMEntry(poa);
      putEntry(key, result);
    }

    return result;
  }

  public final Servant getServant(AOMEntry entry) {
    return (Servant) entryToServant.get(entry);
  }

  public abstract Key getKey(AOMEntry value) throws WrongPolicy;

  public Key getKey(Servant value) throws WrongPolicy {
    AOMEntry entry = (AOMEntry) servantToEntry.get(value);
    return getKey(entry);
  }

  protected void putEntry(Key key, AOMEntry value) {
    keyToEntry.put(key, value);
  }

  public final void putServant(Servant servant, AOMEntry value) {
    entryToServant.put(value, servant);
    servantToEntry.put(servant, value);
  }

  protected abstract void removeEntry(AOMEntry entry, Key key);

  public final void remove(Key key) {
    AOMEntry entry = (AOMEntry) keyToEntry.remove(key);
    Servant servant = (Servant) entryToServant.remove(entry);
    if (servant != null) {
      servantToEntry.remove(servant);
    }

    removeEntry(entry, key);
  }

  public abstract boolean hasMultipleIDs(AOMEntry value);

  protected void clear() {
    keyToEntry.clear();
  }

  public final Set keySet() {
    return keyToEntry.keySet();
  }
}

class SingleObjectMap extends ActiveObjectMap {

  private Map entryToKey = new HashMap();    // Map< AOMEntry, Key >

  public SingleObjectMap(POAImpl poa) {
    super(poa);
  }

  public Key getKey(AOMEntry value) throws WrongPolicy {
    return (Key) entryToKey.get(value);
  }

  protected void putEntry(Key key, AOMEntry value) {
    super.putEntry(key, value);

    entryToKey.put(value, key);
  }

  public boolean hasMultipleIDs(AOMEntry value) {
    return false;
  }

  // This case does not need the key.
  protected void removeEntry(AOMEntry entry, Key key) {
    entryToKey.remove(entry);
  }

  public void clear() {
    super.clear();
    entryToKey.clear();
  }
}

class MultipleObjectMap extends ActiveObjectMap {

  private Map entryToKeys = new HashMap();   // Map< AOMEntry, Set< Key > >

  public MultipleObjectMap(POAImpl poa) {
    super(poa);
  }

  public Key getKey(AOMEntry value) throws WrongPolicy {
    throw new WrongPolicy();
  }

  protected void putEntry(Key key, AOMEntry value) {
    super.putEntry(key, value);

    Set set = (Set) entryToKeys.get(value);
    if (set == null) {
      set = new HashSet();
      entryToKeys.put(value, set);
    }
    set.add(key);
  }

  public boolean hasMultipleIDs(AOMEntry value) {
    Set set = (Set) entryToKeys.get(value);
    if (set == null) {
      return false;
    }
    return set.size() > 1;
  }

  protected void removeEntry(AOMEntry entry, Key key) {
    Set keys = (Set) entryToKeys.get(entry);
    if (keys != null) {
      keys.remove(key);
      if (keys.isEmpty()) {
        entryToKeys.remove(entry);
      }
    }
  }

  public void clear() {
    super.clear();
    entryToKeys.clear();
  }
}
